"""
核心数据模型

包含项目资源规划系统的所有核心数据结构，专注于Excel输入输出格式。
"""

from dataclasses import dataclass, field
from typing import Dict, List, Any, Optional, Union
from datetime import datetime
import re


@dataclass
class ProjectInfo:
    """
    项目基本信息
    
    存储从Excel导入的项目基础数据，包括项目代码、名称、时间范围等信息。
    """
    project_code: str  # 项目代码，如 "PRJ001"
    project_name: str  # 项目名称
    start_month: str   # 开始月份，格式为 "YYYY-MM"，如 "2024-01"
    end_month: str     # 结束月份，格式为 "YYYY-MM"，如 "2024-12"
    priority: int = 1  # 项目优先级，1-5，数字越小优先级越高
    description: str = ""  # 项目描述
    
    def validate(self) -> bool:
        """
        验证项目信息的有效性
        
        Returns:
            bool: 验证是否通过
        """
        # 验证项目代码格式
        if not self.project_code or len(self.project_code.strip()) == 0:
            return False
            
        # 验证项目名称
        if not self.project_name or len(self.project_name.strip()) == 0:
            return False
            
        # 验证月份格式
        month_pattern = r'^\d{4}-\d{2}$'
        if not re.match(month_pattern, self.start_month):
            return False
        if not re.match(month_pattern, self.end_month):
            return False
            
        # 验证开始月份不能晚于结束月份
        if self.start_month > self.end_month:
            return False
            
        # 验证优先级范围
        if not (1 <= self.priority <= 5):
            return False
            
        return True
    
    def get_month_range(self) -> List[str]:
        """
        获取项目涵盖的所有月份列表
        
        Returns:
            List[str]: 月份列表，格式为 ["YYYY-MM", ...]
        """
        months = []
        start_year, start_month = map(int, self.start_month.split('-'))
        end_year, end_month = map(int, self.end_month.split('-'))
        
        current_year, current_month = start_year, start_month
        
        while (current_year, current_month) <= (end_year, end_month):
            months.append(f"{current_year:04d}-{current_month:02d}")
            current_month += 1
            if current_month > 12:
                current_month = 1
                current_year += 1
                
        return months
    
    def to_dict(self) -> Dict[str, Any]:
        """
        转换为字典格式，用于JSON序列化或Excel导出
        
        Returns:
            Dict[str, Any]: 项目信息字典
        """
        return {
            'project_code': self.project_code,
            'project_name': self.project_name,
            'start_month': self.start_month,
            'end_month': self.end_month,
            'priority': self.priority,
            'description': self.description
        }
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'ProjectInfo':
        """
        从字典创建ProjectInfo实例，用于JSON反序列化或Excel导入
        
        Args:
            data (Dict[str, Any]): 包含项目信息的字典
            
        Returns:
            ProjectInfo: 项目信息实例
        """
        return cls(
            project_code=str(data.get('project_code', '')),
            project_name=str(data.get('project_name', '')),
            start_month=str(data.get('start_month', '')),
            end_month=str(data.get('end_month', '')),
            priority=int(data.get('priority', 1)),
            description=str(data.get('description', ''))
        )


@dataclass
class ProjectRole:
    """
    项目角色信息
    
    定义项目中需要的角色类型、数量和技能要求。
    """
    project_code: str  # 所属项目代码
    role_name: str     # 角色名称，如 "前端开发工程师"
    required_count: int  # 需要人数
    skill_requirements: List[str] = field(default_factory=list)  # 技能要求列表
    experience_level: str = "中级"  # 经验要求：初级、中级、高级、资深
    work_ratio: float = 1.0  # 工作占比，0.0-1.0，表示在项目中的工作比例
    
    def validate(self) -> bool:
        """
        验证角色信息的有效性
        
        Returns:
            bool: 验证是否通过
        """
        # 验证项目代码
        if not self.project_code or len(self.project_code.strip()) == 0:
            return False
            
        # 验证角色名称
        if not self.role_name or len(self.role_name.strip()) == 0:
            return False
            
        # 验证人数
        if self.required_count <= 0:
            return False
            
        # 验证经验等级
        valid_levels = ["初级", "中级", "高级", "资深"]
        if self.experience_level not in valid_levels:
            return False
            
        # 验证工作占比
        if not (0.0 <= self.work_ratio <= 1.0):
            return False
            
        return True
    
    def to_dict(self) -> Dict[str, Any]:
        """
        转换为字典格式
        
        Returns:
            Dict[str, Any]: 角色信息字典
        """
        return {
            'project_code': self.project_code,
            'role_name': self.role_name,
            'required_count': self.required_count,
            'skill_requirements': self.skill_requirements.copy(),
            'experience_level': self.experience_level,
            'work_ratio': self.work_ratio
        }
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'ProjectRole':
        """
        从字典创建ProjectRole实例
        
        Args:
            data (Dict[str, Any]): 包含角色信息的字典
            
        Returns:
            ProjectRole: 角色信息实例
        """
        skill_requirements = data.get('skill_requirements', [])
        if isinstance(skill_requirements, str):
            # 如果是字符串，按逗号分割
            skill_requirements = [s.strip() for s in skill_requirements.split(',') if s.strip()]
        
        return cls(
            project_code=str(data.get('project_code', '')),
            role_name=str(data.get('role_name', '')),
            required_count=int(data.get('required_count', 1)),
            skill_requirements=skill_requirements,
            experience_level=str(data.get('experience_level', '中级')),
            work_ratio=float(data.get('work_ratio', 1.0))
        )


@dataclass
class PhaseDetail:
    """
    阶段详细信息
    
    描述项目中的具体阶段，包括时间安排和人力需求。
    """
    project_code: str  # 所属项目代码
    phase_name: str    # 阶段名称，如 "需求分析"、"系统设计"
    start_month: str   # 阶段开始月份，格式为 "YYYY-MM"
    end_month: str     # 阶段结束月份，格式为 "YYYY-MM"
    role_allocations: Dict[str, int] = field(default_factory=dict)  # 角色分配，{角色名: 人数}
    dependencies: List[str] = field(default_factory=list)  # 依赖的阶段名称列表
    deliverables: List[str] = field(default_factory=list)  # 交付物列表
    
    def validate(self) -> bool:
        """
        验证阶段信息的有效性
        
        Returns:
            bool: 验证是否通过
        """
        # 验证项目代码
        if not self.project_code or len(self.project_code.strip()) == 0:
            return False
            
        # 验证阶段名称
        if not self.phase_name or len(self.phase_name.strip()) == 0:
            return False
            
        # 验证月份格式
        month_pattern = r'^\d{4}-\d{2}$'
        if not re.match(month_pattern, self.start_month):
            return False
        if not re.match(month_pattern, self.end_month):
            return False
            
        # 验证开始月份不能晚于结束月份
        if self.start_month > self.end_month:
            return False
            
        # 验证角色分配中的人数
        for role_name, count in self.role_allocations.items():
            if count < 0:
                return False
                
        return True
    
    def get_month_range(self) -> List[str]:
        """
        获取阶段涵盖的所有月份列表
        
        Returns:
            List[str]: 月份列表，格式为 ["YYYY-MM", ...]
        """
        months = []
        start_year, start_month = map(int, self.start_month.split('-'))
        end_year, end_month = map(int, self.end_month.split('-'))
        
        current_year, current_month = start_year, start_month
        
        while (current_year, current_month) <= (end_year, end_month):
            months.append(f"{current_year:04d}-{current_month:02d}")
            current_month += 1
            if current_month > 12:
                current_month = 1
                current_year += 1
                
        return months
    
    def get_total_required_people(self) -> int:
        """
        获取阶段所需的总人数
        
        Returns:
            int: 总人数
        """
        return sum(self.role_allocations.values())
    
    def to_dict(self) -> Dict[str, Any]:
        """
        转换为字典格式
        
        Returns:
            Dict[str, Any]: 阶段信息字典
        """
        return {
            'project_code': self.project_code,
            'phase_name': self.phase_name,
            'start_month': self.start_month,
            'end_month': self.end_month,
            'role_allocations': self.role_allocations.copy(),
            'dependencies': self.dependencies.copy(),
            'deliverables': self.deliverables.copy()
        }
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'PhaseDetail':
        """
        从字典创建PhaseDetail实例
        
        Args:
            data (Dict[str, Any]): 包含阶段信息的字典
            
        Returns:
            PhaseDetail: 阶段信息实例
        """
        role_allocations = data.get('role_allocations', {})
        if isinstance(role_allocations, dict):
            # 确保人数为整数
            role_allocations = {k: int(v) for k, v in role_allocations.items()}
        
        dependencies = data.get('dependencies', [])
        if isinstance(dependencies, str):
            dependencies = [s.strip() for s in dependencies.split(',') if s.strip()]
            
        deliverables = data.get('deliverables', [])
        if isinstance(deliverables, str):
            deliverables = [s.strip() for s in deliverables.split(',') if s.strip()]
        
        return cls(
            project_code=str(data.get('project_code', '')),
            phase_name=str(data.get('phase_name', '')),
            start_month=str(data.get('start_month', '')),
            end_month=str(data.get('end_month', '')),
            role_allocations=role_allocations,
            dependencies=dependencies,
            deliverables=deliverables
        )


@dataclass
class MonthlyAllocation:
    """
    月度分配结果
    
    存储资源分配算法的输出结果，记录每个月各项目的人员分配情况。
    """
    month: str  # 月份，格式为 "YYYY-MM"
    project_code: str  # 项目代码
    role_name: str     # 角色名称
    allocated_count: int  # 分配的人数
    required_count: int   # 需要的人数
    allocation_ratio: float = 0.0  # 分配比例，allocated_count / required_count
    resource_ids: List[str] = field(default_factory=list)  # 分配的资源ID列表
    is_satisfied: bool = False  # 是否满足需求
    
    def __post_init__(self):
        """
        初始化后处理，计算分配比例和满足状态
        """
        if self.required_count > 0:
            self.allocation_ratio = self.allocated_count / self.required_count
            self.is_satisfied = self.allocated_count >= self.required_count
        else:
            self.allocation_ratio = 0.0
            self.is_satisfied = True
    
    def validate(self) -> bool:
        """
        验证分配结果的有效性
        
        Returns:
            bool: 验证是否通过
        """
        # 验证月份格式
        month_pattern = r'^\d{4}-\d{2}$'
        if not re.match(month_pattern, self.month):
            return False
            
        # 验证项目代码
        if not self.project_code or len(self.project_code.strip()) == 0:
            return False
            
        # 验证角色名称
        if not self.role_name or len(self.role_name.strip()) == 0:
            return False
            
        # 验证人数
        if self.allocated_count < 0 or self.required_count < 0:
            return False
            
        # 验证分配比例
        if self.allocation_ratio < 0:
            return False
            
        return True
    
    def get_shortage(self) -> int:
        """
        获取人员缺口数量
        
        Returns:
            int: 缺口数量，正数表示缺人，0表示满足需求
        """
        return max(0, self.required_count - self.allocated_count)
    
    def to_dict(self) -> Dict[str, Any]:
        """
        转换为字典格式
        
        Returns:
            Dict[str, Any]: 分配结果字典
        """
        return {
            'month': self.month,
            'project_code': self.project_code,
            'role_name': self.role_name,
            'allocated_count': self.allocated_count,
            'required_count': self.required_count,
            'allocation_ratio': self.allocation_ratio,
            'resource_ids': self.resource_ids.copy(),
            'is_satisfied': self.is_satisfied
        }
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'MonthlyAllocation':
        """
        从字典创建MonthlyAllocation实例
        
        Args:
            data (Dict[str, Any]): 包含分配结果的字典
            
        Returns:
            MonthlyAllocation: 分配结果实例
        """
        resource_ids = data.get('resource_ids', [])
        if isinstance(resource_ids, str):
            resource_ids = [s.strip() for s in resource_ids.split(',') if s.strip()]
        
        return cls(
            month=str(data.get('month', '')),
            project_code=str(data.get('project_code', '')),
            role_name=str(data.get('role_name', '')),
            allocated_count=int(data.get('allocated_count', 0)),
            required_count=int(data.get('required_count', 0)),
            allocation_ratio=float(data.get('allocation_ratio', 0.0)),
            resource_ids=resource_ids,
            is_satisfied=bool(data.get('is_satisfied', False))
        )


@dataclass
class ValidationResult:
    """
    验证结果
    
    存储数据验证和业务规则检查的结果，用于输出验证报告。
    """
    is_valid: bool  # 整体验证是否通过
    errors: List[str] = field(default_factory=list)      # 错误信息列表
    warnings: List[str] = field(default_factory=list)    # 警告信息列表
    suggestions: List[str] = field(default_factory=list) # 建议信息列表
    validated_at: str = field(default_factory=lambda: datetime.now().isoformat())  # 验证时间
    
    def add_error(self, error_message: str):
        """
        添加错误信息
        
        Args:
            error_message (str): 错误信息
        """
        self.errors.append(error_message)
        self.is_valid = False
    
    def add_warning(self, warning_message: str):
        """
        添加警告信息
        
        Args:
            warning_message (str): 警告信息
        """
        self.warnings.append(warning_message)
    
    def add_suggestion(self, suggestion_message: str):
        """
        添加建议信息
        
        Args:
            suggestion_message (str): 建议信息
        """
        self.suggestions.append(suggestion_message)
    
    def has_errors(self) -> bool:
        """
        检查是否有错误
        
        Returns:
            bool: 是否有错误
        """
        return len(self.errors) > 0
    
    def has_warnings(self) -> bool:
        """
        检查是否有警告
        
        Returns:
            bool: 是否有警告
        """
        return len(self.warnings) > 0
    
    def get_summary(self) -> str:
        """
        获取验证结果摘要
        
        Returns:
            str: 验证结果摘要文本
        """
        summary_parts = []
        
        if self.is_valid:
            summary_parts.append("[成功] 验证通过")
        else:
            summary_parts.append("[失败] 验证失败")
        
        if self.errors:
            summary_parts.append(f"{len(self.errors)} 个错误")
        
        if self.warnings:
            summary_parts.append(f"{len(self.warnings)} 个警告")
        
        if self.suggestions:
            summary_parts.append(f"{len(self.suggestions)} 个建议")
        
        return ", ".join(summary_parts)
    
    def to_dict(self) -> Dict[str, Any]:
        """
        转换为字典格式
        
        Returns:
            Dict[str, Any]: 验证结果字典
        """
        return {
            'is_valid': self.is_valid,
            'errors': self.errors.copy(),
            'warnings': self.warnings.copy(),
            'suggestions': self.suggestions.copy(),
            'validated_at': self.validated_at
        }
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'ValidationResult':
        """
        从字典创建ValidationResult实例
        
        Args:
            data (Dict[str, Any]): 包含验证结果的字典
            
        Returns:
            ValidationResult: 验证结果实例
        """
        return cls(
            is_valid=bool(data.get('is_valid', True)),
            errors=list(data.get('errors', [])),
            warnings=list(data.get('warnings', [])),
            suggestions=list(data.get('suggestions', [])),
            validated_at=str(data.get('validated_at', datetime.now().isoformat()))
        )